package com.atguigu.tingshu.search.service.impl;

import cn.hutool.Hutool;
import cn.hutool.core.util.PageUtil;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch.core.search.*;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.base.BaseEntity;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.vo.album.AlbumInfoIndexSearchVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.google.common.collect.Lists;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.NestedQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.knn_search.KnnSearchQuery;
import co.elastic.clients.util.ObjectBuilder;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.javassist.expr.NewArray;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.FileDescriptor;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class SearchServiceImpl implements SearchService {
    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;
    @Autowired
    private AlbumIndexRepository albumIndexRepository;
    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private CategoryFeignClient categoryFeignClient;
    @Autowired
    private SuggestIndexRepository suggestIndexRepository;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;


    private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            16,
            16,
            60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(50),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    /**
     * 上架专辑
     *
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        CompletableFuture<AlbumInfo> f1 = CompletableFuture.supplyAsync(() -> {

            //远程调用查讯专辑基本信息并封装
            AlbumInfo albumInfo = albumInfoFeignClient.getAlbumInfo1(albumId).getData();
            BeanUtils.copyProperties(albumInfo, albumInfoIndex);
            return albumInfo;
        }, threadPoolExecutor);


        CompletableFuture<Void> f2 = f1.thenAcceptAsync(albumInfo -> {
            //查询专辑作者名并封装
            Long userId = albumInfo.getUserId();
            String nickName = userInfoFeignClient.userNickNameById(userId).getData();
            albumInfoIndex.setAnnouncerName(nickName);
        }, threadPoolExecutor);

        CompletableFuture<Void> f5 = f1.thenAcceptAsync(albumInfo -> {
            BaseCategoryView baseCategoryView = categoryFeignClient.getByCategory3Id(albumInfo.getCategory3Id()).getData();
            Long category1Id = baseCategoryView.getCategory1Id();
            Long category2Id = baseCategoryView.getCategory2Id();
            albumInfoIndex.setCategory1Id(category1Id);
            albumInfoIndex.setCategory2Id(category2Id);
        }, threadPoolExecutor);

        CompletableFuture<Void> f3 = CompletableFuture.runAsync(() -> {
            //远程调用查讯专辑统计量并封装
            AlbumStatVo albumStatVo = albumInfoFeignClient.getAlbumStatByAlbumId(albumId).getData();
            Integer playStatNum = albumStatVo.getPlayStatNum();
            Integer subscribeStatNum = albumStatVo.getSubscribeStatNum();
            Integer buyStatNum = albumStatVo.getBuyStatNum();
            Integer commentStatNum = albumStatVo.getCommentStatNum();
            albumInfoIndex.setPlayStatNum(playStatNum);
            albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
            albumInfoIndex.setBuyStatNum(buyStatNum);
            albumInfoIndex.setCommentStatNum(commentStatNum);
            //计算热度值并封装
            Double hotScore = (double) (playStatNum + 2 * subscribeStatNum + 3 * buyStatNum + 4 * commentStatNum);
            albumInfoIndex.setHotScore(hotScore);
        }, threadPoolExecutor);

        CompletableFuture<Void> f4 = CompletableFuture.runAsync(() -> {
            //远程调用查讯专辑属性并封装
            List<AttributeValueIndex> attributeValueIndexList = albumInfoFeignClient.getAttributeValueIndexByAlbumId(albumId).getData();
            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
        }, threadPoolExecutor);

        CompletableFuture.allOf(f2, f3, f4, f5).join();
        albumIndexRepository.save(albumInfoIndex);

        CompletableFuture.runAsync(() -> {
            initSuggestIndex(albumInfoIndex);
        }, threadPoolExecutor);

        //上架专辑时添加专辑id到布隆过滤器中
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        bloomFilter.add(albumId);
    }

    private void initSuggestIndex(AlbumInfoIndex albumInfoIndex) {
        SuggestIndex suggestIndexTitle = new SuggestIndex();
        String id = UUID.randomUUID().toString().replace("-", "");
        suggestIndexTitle.setId(id);
        String title = albumInfoIndex.getAlbumTitle();
        suggestIndexTitle.setTitle(title);
        suggestIndexTitle.setKeyword(new Completion(new String[]{title}));
        suggestIndexTitle.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(title)}));
        suggestIndexTitle.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(title)}));

        suggestIndexRepository.save(suggestIndexTitle);
        //保存专辑简介
        String albumIntro = albumInfoIndex.getAlbumIntro();
        SuggestIndex suggestIndexIntro = new SuggestIndex();
        String id1 = UUID.randomUUID().toString().replace("-", "");
        suggestIndexIntro.setId(id1);
        suggestIndexIntro.setTitle(albumIntro);
        suggestIndexIntro.setKeyword(new Completion(new String[]{albumIntro}));
        suggestIndexIntro.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumIntro)}));
        suggestIndexIntro.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumIntro)}));

        suggestIndexRepository.save(suggestIndexIntro);
        //保存专辑
        String announcerName = albumInfoIndex.getAnnouncerName();
        SuggestIndex suggestIndexName = new SuggestIndex();
        String id2 = UUID.randomUUID().toString().replace("-", "");
        suggestIndexName.setId(id2);
        suggestIndexName.setTitle(announcerName);
        suggestIndexName.setKeyword(new Completion(new String[]{announcerName}));
        suggestIndexName.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(announcerName)}));
        suggestIndexName.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(announcerName)}));
        suggestIndexRepository.save(suggestIndexName);
    }

    @Override
    public void lowerAlbum(Long albumId) {

        albumIndexRepository.deleteById(albumId);

        //todo:专辑下架后从布隆过滤器中删除
        //创建新的布隆过滤器
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER_NEW);
        //从es中获取专辑id
        albumIndexRepository.findAll().forEach(albumInfoIndex -> {
            bloomFilter.add(albumInfoIndex.getId());
        });

        //使用lua脚本对旧布隆过滤器进行删除，然后将新的布隆过滤器进行改名
        String script = "redis.call(\"del\" , KEYS[1])\n" +
                "redis.call(\"del\" , \"{\"..KEYS[1]..\"}:\"..\"config\")\n" +
                "redis.call(\"rename\" , KEYS[2] , KEYS[1])\n" +
                "redis.call(\"rename\" , \"{\"..KEYS[2]..\"}:\"..\"config\" , \"{\"..KEYS[1]..\"}:\"..\"config\")\n" +
                "return 1";
        redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Lists.newArrayList(RedisConstant.ALBUM_BLOOM_FILTER, RedisConstant.ALBUM_BLOOM_FILTER_NEW));
        log.info("布隆过滤器替换成功");
    }


    /**
     * 通过Elasticsearch搜索专辑
     *
     * @param albumIndexQuery
     * @return
     */
    @Override
    @SneakyThrows
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        //TODO:构建searchQuery
        SearchRequest searchRequest = searchRequest(albumIndexQuery);
//        System.out.println(searchRequest.toString());
        SearchResponse<AlbumInfoIndex> search = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        //todo:处理结果
        AlbumSearchResponseVo albumSearchResponseVo = parseResponse(search, albumIndexQuery);

        return albumSearchResponseVo;
    }

    /**
     * 获取频道页数据
     *
     * @param category1Id
     * @return
     */
    @Override
    @SneakyThrows
    public List<AlbumInfoIndexSearchVo> channel(Long category1Id) {
        //远程调用根据一级分类id来获取置顶的三级分类数据
        List<BaseCategory3> baseCategory3s = categoryFeignClient.findTopBaseCategory3(category1Id).getData();

        Map<Long, BaseCategory3> baseCategory3Map = baseCategory3s.stream().collect(Collectors.toMap(baseCategory3 -> baseCategory3.getId(), baseCategory3 -> baseCategory3));

        List<FieldValue> fieldValues = baseCategory3s.stream().map(baseCategory3 -> {
            Long id = baseCategory3.getId();
            return new FieldValue.Builder().longValue(id).build();
        }).toList();

        BoolQuery.Builder builder = new BoolQuery.Builder();
        Query query1 = new Query.Builder().term(builder1 -> builder1.field("category1Id").value(category1Id)).build();
        builder.must(query1);
        Query query2 = new Query.Builder().terms(builder1 -> builder1.field("category3Id").terms(t -> t.value(fieldValues))).build();
        builder.must(query2);

        Query query = new Query.Builder().bool(builder.build()).build();

        TermsAggregation termsAggregation = new TermsAggregation.Builder().field("category3Id").size(100).build();

        Aggregation aggregation1 = new Aggregation.Builder().topHits(builder1 -> builder1.size(10)
                .sort(builder2 -> builder2.field(builder3 -> builder3.field("hotScore").order(SortOrder.Desc)))
        ).build();

        Aggregation aggregation = new Aggregation.Builder()
                .terms(termsAggregation)
                .aggregations("topHitsAgg", aggregation1)
                .build();

        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("albuminfo")
                .query(query)
                .source(builder1 -> builder1.fetch(Boolean.FALSE))
                .aggregations("category3IdAgg", aggregation)
                .build();

        //查询频道页数据
        SearchResponse<AlbumInfoIndex> search = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        //获取结果并封装数据
        Map<String, Aggregate> aggregations = search.aggregations();
        Aggregate category3IdAgg = aggregations.get("category3IdAgg");
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate) category3IdAgg._get();
        Buckets<LongTermsBucket> buckets = longTermsAggregate.buckets();
        List<LongTermsBucket> array = buckets.array();
        List<AlbumInfoIndexSearchVo> albumInfoIndexSearchVos = new ArrayList<>();
        for (LongTermsBucket longTermsBucket : array) {
            //三级分类Id
            long c3Id = longTermsBucket.key();
            Map<String, Aggregate> aggregations1 = longTermsBucket.aggregations();
            Aggregate topHitsAgg = aggregations1.get("topHitsAgg");
            TopHitsAggregate topHitsAggregate = (TopHitsAggregate) topHitsAgg._get();
            List<Hit<JsonData>> hits = topHitsAggregate.hits().hits();
            List<AlbumInfoIndex> albumInfoIndices = hits.stream().map(hit -> {
                AlbumInfoIndex albumInfoIndex = hit.source().to(AlbumInfoIndex.class);
                return albumInfoIndex;
            }).toList();
            AlbumInfoIndexSearchVo albumInfoIndexSearchVo = new AlbumInfoIndexSearchVo();
            albumInfoIndexSearchVo.setBaseCategory3(baseCategory3Map.get(c3Id));
            albumInfoIndexSearchVo.setList(albumInfoIndices);
            albumInfoIndexSearchVos.add(albumInfoIndexSearchVo);
        }

        return albumInfoIndexSearchVos;
    }

    /**
     * 自动补全功能
     *
     * @param keyword
     * @return
     */
    @Override
    @SneakyThrows
    public List<String> completeSuggest(String keyword) {

        CompletionSuggester keywordCompletion = new CompletionSuggester.Builder().field("keyword").skipDuplicates(true).size(10).fuzzy(builder -> builder.fuzziness("auto")).build();
        FieldSuggester fieldSuggester = new FieldSuggester.Builder().prefix(keyword).completion(keywordCompletion).build();

        CompletionSuggester keywordPinyinCompletion = new CompletionSuggester.Builder().field("keywordPinyin").skipDuplicates(true).size(10).fuzzy(builder -> builder.fuzziness("auto")).build();
        FieldSuggester fieldSuggester1 = new FieldSuggester.Builder().prefix(PinYinUtils.toHanyuPinyin(keyword)).completion(keywordPinyinCompletion).build();

        CompletionSuggester completionSuggester = new CompletionSuggester.Builder().field("keywordSequence").skipDuplicates(true).size(10).fuzzy(builder -> builder.fuzziness("auto")).build();
        FieldSuggester fieldSuggester2 = new FieldSuggester.Builder().prefix(PinYinUtils.getFirstLetter(keyword)).completion(completionSuggester).build();
        Suggester suggester = new Suggester.Builder()
                .suggesters("suggestionKeyword", fieldSuggester)
                .suggesters("suggestionkeywordPinyin", fieldSuggester1)
                .suggesters("suggestionkeywordSequence", fieldSuggester2)
                .build();

        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("suggestinfo")
                .suggest(suggester)
                .build();
        //查询es并封装结果
        List<String> TitleList = new ArrayList<>();
        SearchResponse<SuggestIndex> search = elasticsearchClient.search(searchRequest, SuggestIndex.class);
        Map<String, List<Suggestion<SuggestIndex>>> suggest = search.suggest();
        List<Suggestion<SuggestIndex>> suggestionKeyword = suggest.get("suggestionKeyword");
        suggestionKeyword.stream().forEach(suggestIndexSuggestion -> {
            CompletionSuggest<SuggestIndex> completion = suggestIndexSuggestion.completion();
            List<CompletionSuggestOption<SuggestIndex>> options = completion.options();
            for (CompletionSuggestOption<SuggestIndex> option : options) {
                SuggestIndex source = option.source();
                String title = source.getTitle();
                TitleList.add(title);
            }
        });
        return TitleList;
    }

    /**
     * 更新排行榜
     */
    @Override
    @SneakyThrows
    public void updateLatelyAlbumRanking() {

        TermsAggregation termsAggregation = new TermsAggregation.Builder().field("category1Id").size(100).build();
        Aggregation.Builder.ContainerBuilder terms = new Aggregation.Builder()
                .terms(termsAggregation);

        List<String> list = Arrays.stream(new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"}).toList();

        list.forEach(fieldName -> {
            Aggregation aggregation = new Aggregation.Builder()
                    .topHits(builder -> builder.size(10).sort(builder1 -> builder1.field(builder2 -> builder2.field(fieldName).order(SortOrder.Desc))))
                    .build();
            terms.aggregations(fieldName + "TopHitsAgg", aggregation);
        });


        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("albuminfo")
                .query(new Query.Builder().matchAll(builder -> builder).build())
                .aggregations("category1IdAgg", terms.build())
                .build();

        //查询获取结果
        SearchResponse<AlbumInfoIndex> search = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate) search.aggregations().get("category1IdAgg")._get();
        List<LongTermsBucket> bucketList = longTermsAggregate.buckets().array();
        bucketList.forEach(longTermsBucket -> {
            List<LongTermsBucket> array = longTermsAggregate.buckets().array();
            array.forEach(longTermsBucket1 -> {
                long c1Id = longTermsBucket1.key();
                Map<String, Aggregate> aggregations = longTermsBucket1.aggregations();
                list.forEach(fieldName -> {
                    TopHitsAggregate topHitsAggregate = (TopHitsAggregate) aggregations.get(fieldName + "TopHitsAgg")._get();
                    List<Hit<JsonData>> hits = topHitsAggregate.hits().hits();
                    List<AlbumInfoIndex> albumInfoIndices = hits.stream().map(hit -> {
                        AlbumInfoIndex albumInfoIndex = hit.source().to(AlbumInfoIndex.class);
                        return albumInfoIndex;
                    }).toList();
                    //todo:将数据保存到redis中
                    HashOperations<String, Object, Object> stringObjectObjectHashOperations = redisTemplate.opsForHash();
                    String bigKey = "album:ranking:" + ":" + c1Id;
                    String minKey = fieldName;
                    stringObjectObjectHashOperations.put(bigKey, minKey, JSON.toJSONString(albumInfoIndices));
                });
            });
        });

    }

    /**
     * 获取排行榜
     *
     * @param category1Id
     * @param dimension
     * @return
     */
    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long category1Id, String dimension) {
        String bigKey = "album:ranking:" + ":" + category1Id;
        String minKey = dimension;
        HashOperations<String, Object, Object> stringObjectObjectHashOperations = redisTemplate.opsForHash();
        String o = (String) stringObjectObjectHashOperations.get(bigKey, minKey);
        List<AlbumInfoIndex> albumInfoIndices = JSON.parseArray(o, AlbumInfoIndex.class);
        List<AlbumInfoIndexVo> list = albumInfoIndices.stream().map(albumInfoIndex -> {
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
            return albumInfoIndexVo;
        }).toList();
        return list;
    }

    /**
     * 处理结果
     *
     * @param search
     * @param albumIndexQuery
     * @return
     */
    private AlbumSearchResponseVo parseResponse(SearchResponse<AlbumInfoIndex> search, AlbumIndexQuery albumIndexQuery) {

        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        List<Hit<AlbumInfoIndex>> hits = search.hits().hits();
        List<AlbumInfoIndexVo> list1 = hits.stream().map(hit -> {
            AlbumInfoIndex albumInfoIndex = hit.source();
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);

            //获取高亮结果
            Map<String, List<String>> highlight = hit.highlight();
            if (!CollectionUtils.isEmpty(highlight)) {
                List<String> list = highlight.get("albumTitle");
                if (!CollectionUtils.isEmpty(list)) {
                    albumInfoIndexVo.setAlbumTitle(list.get(0));
                }
            }
            return albumInfoIndexVo;
        }).toList();
        albumSearchResponseVo.setList(list1);
        albumSearchResponseVo.setTotal(search.hits().total().value());
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        int i = PageUtil.totalPage(albumSearchResponseVo.getTotal(), albumSearchResponseVo.getPageSize());
        albumSearchResponseVo.setTotalPages(Long.parseLong(String.valueOf(i)));
        return albumSearchResponseVo;
    }

    /**
     * 构建SearchRequest
     *
     * @param albumIndexQuery
     * @return
     */
    private SearchRequest searchRequest(AlbumIndexQuery albumIndexQuery) {

        BoolQuery.Builder builder = new BoolQuery.Builder();

        if (!StringUtils.isEmpty(albumIndexQuery.getKeyword())) {
            builder.must(builder1 -> builder1.multiMatch(m -> m.query(albumIndexQuery.getKeyword()).fields("albumTitle", "albumIntro")));
        } else {
            builder.must(builder1 -> builder1.matchAll(m -> m));
        }

        if (albumIndexQuery.getCategory1Id() != null) {
            builder.must(builder1 -> builder1.term(t -> t.field("category1Id").value(albumIndexQuery.getCategory1Id())));
        }
        if (albumIndexQuery.getCategory2Id() != null) {
            builder.must(builder1 -> builder1.term(t -> t.field("category2Id").value(albumIndexQuery.getCategory2Id())));
        }
        if (albumIndexQuery.getCategory3Id() != null) {
            builder.must(builder1 -> builder1.term(t -> t.field("category3Id").value(albumIndexQuery.getCategory3Id())));
        }

        if (!CollectionUtils.isEmpty(albumIndexQuery.getAttributeList())) {
            List<String> attributeList = albumIndexQuery.getAttributeList();
            for (String attribute : attributeList) {
                String[] split = attribute.split(":");

                BoolQuery boolQuery = new BoolQuery.Builder().must(builder1 -> builder1.term(t -> t.field("attributeValueIndexList.attributeId").value(split[0])))
                        .must(builder1 -> builder1.term(t -> t.field("attributeValueIndexList.ValueId").value(split[1]))).build();

                Query query1 = new Query.Builder().bool(boolQuery).build();

                NestedQuery nestedQuery = new NestedQuery.Builder().path("attributeValueIndexList").query(query1).build();
                Query query = new Query.Builder().nested(nestedQuery).build();
                builder.must(query);
            }
        }

        Query query = new Query.Builder()
                .bool(builder.build())
                .build();


        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        Integer from = (pageNo - 1) * pageSize;
        SearchRequest.Builder albuminfoSearch = new SearchRequest.Builder()
                .index("albuminfo")
                .query(query)
                .from(from)
                .size(pageSize);
        if (!StringUtils.isEmpty(albumIndexQuery.getOrder())) {

            SortOptions.Builder builder1 = new SortOptions.Builder();
            String[] split = albumIndexQuery.getOrder().split(":");

            switch (split[0]) {
                case "1":
                    SortOrder sortOrder = split[1].equals("asc") ? SortOrder.Asc : SortOrder.Desc;
                    builder1.field(builder2 -> builder2.field("hotScore").order(sortOrder));
                    break;
                case "2":
                    SortOrder sortOrder1 = split[1].equals("asc") ? SortOrder.Asc : SortOrder.Desc;
                    builder1.field(builder2 -> builder2.field("playStatNum").order(sortOrder1));
                    break;
                case "3":
                    SortOrder sortOrder2 = split[1].equals("asc") ? SortOrder.Asc : SortOrder.Desc;
                    builder1.field(builder2 -> builder2.field("createTime").order(sortOrder2));
            }
            albuminfoSearch.sort(builder1.build());
        }

        String keyword = albumIndexQuery.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            HighlightField highlightField = new HighlightField.Builder().preTags("<font color='red'>").postTags("</font>").build();
            Highlight highlight = new Highlight.Builder().fields("albumTitle", highlightField)
                    .fields("albumIntro", highlightField)
                    .build();
            albuminfoSearch.highlight(highlight);
        }

        return albuminfoSearch.build();
    }
}
